创建聊天机器人 (ChatInterface)
聊天机器人是大型语言模型 (LLM) 的一个流行应用。通过 Gradio,您可以轻松构建聊天机器人模型的演示并与用户分享,或者使用直观的聊天界面自己试用。
本教程使用 gr.ChatInterface()
,这是一个高级抽象,可让您快速创建聊天机器人 UI,通常只需几行代码即可完成。构建的聊天机器人界面包含了现代化的设计,支持消息历史、流式输出和多模态交互等功能。
定义聊天函数
使用 gr.ChatInterface()
创建聊天机器人的第一步是定义您的聊天函数。在最简单的情况下,您的聊天函数应该接受两个参数:message
和 history
(参数可以任意命名,但必须按此顺序)。
message
: 一个str
字符串,表示用户的最新消息。history
: 一个list
,包含 OpenAI 风格的消息字典,每个字典有role
和content
键,代表到该点为止的对话历史。
示例 history
可能如下所示:
[
{"role": "user", "content": "法国的首都是什么?"},
{"role": "assistant", "content": "巴黎"}
]
而下一个 message
可能是:
"它最大的城市是什么?"
您的聊天函数应该返回一个字符串响应,这是机器人对特定用户输入 message
的回复,例如:
"巴黎也是法国最大的城市。"
基础示例:随机回答"是"或"否"的聊天机器人
让我们编写一个简单的聊天功能,随机响应 "是" 或 "否":
import random
import gradio as gr
def random_response(message, history):
return random.choice(["是", "否"])
demo = gr.ChatInterface(random_response, type="messages")
if __name__ == "__main__":
demo.launch()
利用用户输入和历史记录的示例
前面的例子非常简单,它甚至没有考虑用户输入或以前的历史记录!这里是另一个简单的例子,展示如何整合用户的输入以及历史记录:
import gradio as gr
def alternatingly_agree(message, history):
# 根据历史长度决定响应
if len([h for h in history if h['role'] == "assistant"]) % 2 == 0:
return f"是的,我认为'{message}'是对的"
else:
return "我不这么认为"
demo = gr.ChatInterface(alternatingly_agree, type="messages")
if __name__ == "__main__":
demo.launch()
流式聊天机器人(Streaming)
在您的聊天函数中,您可以使用 yield
来生成一系列部分响应,每个响应都会替换之前的响应。这样,您就能创建一个流式聊天机器人,让用户看到回复的生成过程:
import time
import gradio as gr
def slow_echo(message, history):
for i in range(len(message)):
time.sleep(0.3) # 模拟思考时间
yield "您输入的内容: " + message[:i+1]
demo = gr.ChatInterface(slow_echo, type="messages")
if __name__ == "__main__":
demo.launch()
在响应流式传输过程中,"提交"按钮将变为"停止"按钮,可用于停止生成器函数。
定制化聊天机器人
自定义聊天界面外观
与 Gradio 的 Interface
类似,gr.ChatInterface
包含许多相同的参数,您可以使用这些参数来自定义聊天机器人的外观:
- 使用
title
和description
添加标题和描述 - 使用
theme
和css
添加主题或自定义 CSS - 添加
examples
并启用cache_examples
,让用户更容易尝试 - 自定义聊天机器人界面中的按钮(
submit_btn
、retry_btn
、undo_btn
、clear_btn
)
以下是使用这些参数的示例:
import gradio as gr
def yes_man(message, history):
if message.endswith("?"):
return "是的"
else:
return "请随时向我提问!"
demo = gr.ChatInterface(
yes_man,
type="messages",
chatbot=gr.Chatbot(height=300),
textbox=gr.Textbox(placeholder="问我一个是或否的问题", container=False, scale=7),
title="是先生",
description="向'是先生'提出任何问题",
theme="soft",
examples=["你好", "我酷吗?", "番茄是蔬菜吗?"],
cache_examples=True,
retry_btn="重试",
undo_btn="撤销上一条",
clear_btn="清空",
)
if __name__ == "__main__":
demo.launch()
如果您想在用户开始聊天之前显示一个"占位符",可以使用 gr.Chatbot
的 placeholder
参数,它接受 Markdown 或 HTML:
gr.ChatInterface(
yes_man,
type="messages",
chatbot=gr.Chatbot(placeholder="<strong>您的专属'是先生'</strong><br>请随意提问"),
# 其他参数...
)
占位符在聊天机器人中垂直和水平居中显示。
多模态聊天界面
您可能希望为聊天机器人添加多模态功能,允许用户上传图像或文件并询问相关问题。通过向 gr.ChatInterface
传递参数 multimodal=True
即可实现:
import gradio as gr
def count_files(message, history):
num_files = len(message["files"])
return f"您上传了 {num_files} 个文件"
demo = gr.ChatInterface(
fn=count_files,
type="messages",
examples=[{"text": "你好", "files": []}],
title="文件计数机器人",
multimodal=True
)
if __name__ == "__main__":
demo.launch()
当 multimodal=True
时,聊天函数的签名会略有变化:
- 第一个参数应接受一个由提交的文本和上传的文件组成的字典:
{"text": "用户输入", "files": ["文件路径1", "文件路径2", ...]}
- 同样,您提供的任何示例也应位于此形式的字典中
如果您想定制多模态聊天机器人的文本框,您应该将 gr.MultimodalTextbox
的实例传递给 ChatInterface
的 textbox
参数:
demo = gr.ChatInterface(
fn=count_files,
type="messages",
multimodal=True,
textbox=gr.MultimodalTextbox(
file_count="multiple", # 允许多文件上传
file_types=["image"], # 仅允许图像
sources=["upload", "microphone"] # 启用上传和麦克风
)
)
添加额外输入组件
您可能希望向聊天机器人添加其他参数,例如系统提示文本框或控制响应令牌数量的滑块。ChatInterface
支持 additional_inputs
参数来添加其他输入组件:
import gradio as gr
import time
def echo(message, history, system_prompt, tokens):
response = f"系统提示: {system_prompt}\n 消息: {message}."
for i in range(min(len(response), int(tokens))):
time.sleep(0.05)
yield response[: i + 1]
demo = gr.ChatInterface(
echo,
type="messages",
additional_inputs=[
gr.Textbox("您好,我是一个AI助手。", label="系统提示"),
gr.Slider(10, 100, label="生成的最大字符数"),
],
)
if __name__ == "__main__":
demo.launch()
如果您想要更灵活的布局,可以在 gr.Blocks()
中预先定义组件,然后在 additional_inputs
中使用它们:
import gradio as gr
import time
def echo(message, history, system_prompt, tokens):
response = f"系统提示: {system_prompt}\n 消息: {message}."
for i in range(min(len(response), int(tokens))):
time.sleep(0.05)
yield response[: i+1]
with gr.Blocks() as demo:
system_prompt = gr.Textbox("您好,我是一个AI助手。", label="系统提示")
slider = gr.Slider(10, 100, label="生成的最大字符数", render=False)
gr.ChatInterface(
echo,
type="messages",
additional_inputs=[system_prompt, slider]
)
if __name__ == "__main__":
demo.launch()
返回复杂响应
除了返回简单的文本字符串外,您还可以返回更复杂的响应:
返回文件或 Gradio 组件
以下 Gradio 组件可以在聊天界面中显示:
gr.Image
- 图像gr.Plot
- 绘图gr.Audio
- 音频gr.HTML
- HTMLgr.Video
- 视频gr.Gallery
- 图片集gr.File
- 文件
只需从您的函数中返回这些组件之一即可:
import gradio as gr
def music(message, history):
if message.strip():
return gr.Audio("https://github.com/gradio-app/gradio/raw/main/test/test_files/audio_sample.wav")
else:
return "请提供艺术家的名字"
demo = gr.ChatInterface(
music,
type="messages",
textbox=gr.Textbox(placeholder="您想听哪位艺术家的音乐?", scale=7),
)
if __name__ == "__main__":
demo.launch()
返回多条消息
您可以通过返回消息列表来一次发送多条消息,例如一条文本消息加一个文件:
import gradio as gr
def echo_multimodal(message, history):
response = []
response.append(f"您写道: '{message['text']}' 并上传了:")
if message.get("files"):
for file in message["files"]:
response.append(gr.File(value=file))
return response
demo = gr.ChatInterface(
echo_multimodal,
type="messages",
multimodal=True,
textbox=gr.MultimodalTextbox(file_count="multiple"),
)
if __name__ == "__main__":
demo.launch()
用户反馈和历史记录
收集用户反馈
要收集用户对聊天模型的反馈,设置 gr.ChatInterface(flagging_mode="manual")
,用户就可以对助手的响应进行点赞或点踩。每个被标记的响应以及整个聊天历史都会保存在一个 CSV 文件中。
您也可以通过 flagging_options
参数自定义反馈选项。默认选项是"Like"和"Dislike",将显示为点赞和点踩图标:
import gradio as gr
import time
def slow_echo(message, history):
for i in range(len(message)):
time.sleep(0.05)
yield "您输入的内容: " + message[: i + 1]
demo = gr.ChatInterface(
slow_echo,
type="messages",
flagging_mode="manual",
flagging_options=["喜欢", "垃圾信息", "不适当", "其他"],
save_history=True,
)
if __name__ == "__main__":
demo.launch()
启用聊天历史保存
通过设置 gr.ChatInterface(save_history=True)
可以启用持久聊天历史记录,让用户保持多个对话并轻松切换。启用后,对话将存储在用户浏览器的本地存储中,以侧面板形式显示之前的对话。
这意味着多个用户可以同时与同一个 ChatInterface 交互,同时保持自己的私人对话历史。
通过 API 使用聊天机器人
构建 Gradio 聊天机器人并将其托管在 Hugging Face Spaces 或其他地方后,您可以使用 /chat
端点上的简单 API 对其进行查询。端点只需要用户的消息(如果您使用 additional_inputs
参数设置了任何内容,则可能需要其他输入),并将返回响应,内部跟踪迄今为止发送的消息。
要使用端点,您应该使用 Gradio Python 客户端或 Gradio JS 客户端。您也可以将 Chat Interface 部署为 Discord 机器人、Slack 机器人或网站小部件。
与实际语言模型结合
使用 OpenAI API
以下是与 OpenAI API 结合使用的示例,支持流式输出:
import os
import gradio as gr
from openai import OpenAI
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
def predict(message, history):
history_openai_format = []
for human, assistant in history:
history_openai_format.append({"role": "user", "content": human})
history_openai_format.append({"role": "assistant", "content": assistant})
history_openai_format.append({"role": "user", "content": message})
response = client.chat.completions.create(
model='gpt-3.5-turbo',
messages=history_openai_format,
temperature=1.0,
stream=True
)
partial_message = ""
for chunk in response:
if chunk.choices[0].delta.content is not None:
partial_message = partial_message + chunk.choices[0].delta.content
yield partial_message
demo = gr.ChatInterface(predict, type="messages")
if __name__ == "__main__":
demo.launch()
使用 Hugging Face 模型
以下是使用 Hugging Face 的开源 LLM 的示例:
import gradio as gr
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
from threading import Thread
# 加载模型和分词器
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen-7B-Chat")
model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen-7B-Chat",
torch_dtype=torch.float16,
device_map="auto"
)
def predict(message, history):
# 格式化历史记录和当前消息
prompt = tokenizer.apply_chat_template(
[{"role": "user" if i % 2 == 0 else "assistant", "content": m}
for i, m in enumerate([item for sublist in history for item in sublist] + [message])],
tokenize=False,
add_generation_prompt=True
)
# 对提示进行分词
model_inputs = tokenizer([prompt], return_tensors="pt").to(model.device)
# 设置流式传输
streamer = TextIteratorStreamer(tokenizer, timeout=10., skip_prompt=True, skip_special_tokens=True)
# 设置生成参数
generate_kwargs = dict(
model_inputs,
streamer=streamer,
max_new_tokens=1024,
do_sample=True,
top_p=0.95,
top_k=50,
temperature=0.7,
num_beams=1,
)
# 在单独的线程中生成文本
t = Thread(target=model.generate, kwargs=generate_kwargs)
t.start()
# 流式输出结果
partial_message = ""
for new_token in streamer:
partial_message += new_token
yield partial_message
demo = gr.ChatInterface(predict, type="messages")
if __name__ == "__main__":
demo.launch()
结论
通过 Gradio 的 ChatInterface
类,您可以轻松创建功能丰富的聊天机器人应用程序。从简单的文本响应到多模态交互,从基本演示到复杂的 LLM 集成,ChatInterface
提供了灵活且用户友好的方式来展示您的聊天模型。
如果您需要更高级的定制,可以考虑使用 Gradio 的低级 Blocks
API 构建聊天机器人 UI,这将在后续指南中详细介绍。